// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.departures

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import org.openapitools.client.infrastructure.ServerException
import xyz.apiote.bimba.czwek.api.Error
import xyz.apiote.bimba.czwek.data.settings.SettingsRepository
import xyz.apiote.bimba.czwek.network.mapError
import xyz.apiote.bimba.czwek.repo.FeedInfo
import xyz.apiote.bimba.czwek.repo.OnlineRepository
import xyz.apiote.bimba.czwek.repo.QrLocation
import xyz.apiote.bimba.czwek.repo.StopEvents
import xyz.apiote.bimba.czwek.repo.TrafficResponseException
import java.time.LocalDate
import java.time.LocalTime

class DeparturesViewModel : ViewModel() {
	private val _departures = MutableLiveData<StopEvents>()
	val departures: LiveData<StopEvents> = _departures
	private val _error = MutableLiveData<Error>()
	val error: LiveData<Error> = _error
	var requestedItemsNumber = 12
	var allItemsRequested = false
	private var feed: FeedInfo? = null
	var openBottomSheet: DepartureBottomSheet? = null
	private lateinit var code: String
	val mutableLinesFilter = MutableLiveData<Map<String, Boolean>>()
	val linesFilter: LiveData<Map<String, Boolean>> = mutableLinesFilter
	var showingTerminusArrivals: String? = null

	// TODO observe in activity, maybe refreshing and not getting departures is enough
	var startTime: LocalTime = LocalTime.MIN
	var endTime: LocalTime = LocalTime.MAX

	// TODO observe in activity, refreshing is not enough
	var date: LocalDate? = null

	fun getDepartures(context: Context, date: LocalDate?, force: Boolean, exact: Boolean) {
		MainScope().launch {
			try {
				if (feed == null) {
					feed = getFeed(context)
					code = getCode(context, feed!!)
				}

				val repository = OnlineRepository()
				val stopDepartures =
					repository.getDepartures(
						feed!!.id,
						code,
						date,
						context,
						requestedItemsNumber,
						exact
					)
				stopDepartures?.let {
					if (stopDepartures.events.isEmpty()) {
						throw mapError(44, null)
					}
					_departures.value = it
				}
			} catch (e: TrafficResponseException) {
				if (!departures.isInitialized || force) {
					_error.value = e.error
				}
				Log.w("Departures", "$e")
			} catch (e: ServerException) {
				if (!departures.isInitialized || force) {
					_error.value = Error.fromTransitous(e)
				}
				Log.w("Departures", "Transitous returned ${e.statusCode}, ${e.message}")
			}
		}
	}

	private suspend fun getFeed(context: Context): FeedInfo {
		val intent = (context as Activity).intent

		val feeds =
			SettingsRepository().getCachedFeedInfos(context).filter {
				it is FeedInfo
			}.toList().associate { Pair((it as FeedInfo).id, it) }

		return when (intent.action) {
			Intent.ACTION_VIEW -> {
				val feed = feeds.values.find { it.qrHost == intent.data?.host }
				return feed
					?: if (feeds.values.all { it.qrIn == QrLocation.UNKNOWN } == true) {
						getFeedOld(intent.data?.host, feeds) ?: throw TrafficResponseException(41)
					} else {
						throw TrafficResponseException(41)
					}
			}

			null -> {
				val feedID = intent.extras?.getString(DeparturesActivity.FEED_PARAM)
				feeds[feedID] ?: throw TrafficResponseException(41)
			}

			else -> throw TrafficResponseException(41)
		}
	}

	// TODO [after removing FeedsResponseV1] remove this method
	private fun getFeedOld(host: String?, feeds: Map<String, FeedInfo?>): FeedInfo? {
		@Suppress("SpellCheckingInspection")
		return when (host) {
			"www.peka.poznan.pl" -> feeds["poznan_ztm"]
			"rj.metropoliaztm.pl" -> feeds["gzm_ztm"]
			else -> null
		}
	}

	private fun getCode(context: Context, feed: FeedInfo): String {
		val intent = (context as Activity).intent
		return when (intent.action) {
			Intent.ACTION_VIEW -> {
				when (feed.qrIn) {
					QrLocation.QUERY -> {
						intent.data?.getQueryParameter(feed.qrSelector)
					}

					QrLocation.PATH -> {
						feed.qrSelector.toRegex().find(intent.data?.path ?: "")?.value
					}

					QrLocation.UNKNOWN -> {
						getCodeOld(intent.data)
					}

					QrLocation.NONE -> {
						throw TrafficResponseException(41)
					}
				} ?: throw TrafficResponseException(41)
			}

			null -> intent?.extras?.getString(DeparturesActivity.CODE_PARAM)
				?: throw TrafficResponseException(41)

			else -> throw TrafficResponseException(41)
		}
	}

	// TODO [after removing FeedsResponseV1] remove this method
	private fun getCodeOld(data: Uri?): String {
		@Suppress("SpellCheckingInspection")
		return when (data?.host) {
			"www.peka.poznan.pl" -> data.getQueryParameter("przystanek") ?: ""
			"rj.metropoliaztm.pl" -> data.lastPathSegment ?: ""
			else -> ""
		}
	}
}
